use sqlx::{MySql, Pool};

use crate::{entity::mock_router::MockRouter, errors::AdminOpError};

pub async fn update_mock_router(pool: Pool<MySql>, data: MockRouter) -> Result<i32, AdminOpError> {
    let mut tx = pool.begin().await?;

    let res = sqlx::query("select * from mock_routers where id = ?")
        .bind(&data.id)
        .fetch_all(&mut *tx)
        .await?;
    if res.len() == 0 {
        tx.rollback().await?;
        return Err(AdminOpError::RecordNotExist(format!("{}", data.id)));
    }

    if let Ok(res) = sqlx::query("update mock_routers set path=?,route_group=?,method=?,headers=?,status_code=?,content=? where id = ?")
        .bind(&data.route)
        .bind(&data.group)
        .bind(&data.method)
        .bind(&data.serialize_headers())
        .bind(&data.status_code)
        .bind(&data.content).bind(&data.id).execute(&mut *tx).await{
            tx.commit().await?;
            Ok(res.rows_affected() as i32)
    }else{
        tx.rollback().await?;
        Err(AdminOpError::InternalError("open transaction error".to_string()))
    }
}

pub async fn get_mock_routers(pool: Pool<MySql>) -> Result<Vec<MockRouter>, AdminOpError> {
    let rows = sqlx::query("select * from mock_routers")
        .fetch_all(&pool)
        .await?;
    let mut routers = vec![];
    for row in rows {
        let route: MockRouter = row.into();
        routers.push(route);
    }
    Ok(routers)
}

pub async fn add_route(pool: Pool<MySql>, data: MockRouter) -> Result<i32, AdminOpError> {
    let mut tx = pool.begin().await?;
    if let Ok(res) = sqlx::query("select * from mock_routers where id = ?")
        .bind(&data.id)
        .fetch_all(&mut *tx)
        .await
    {
        tx.rollback().await?;
        return Err(AdminOpError::RecordExist(format!("{}", res.len())));
    }
    if let Ok(res) =  sqlx::query("insert into mock_routers (`path`,`route_group`,`method`,`headers`,`status_code`,`content`) values(?,?,?,?,?,?)")
    .bind(&data.route)
    .bind(&data.group)
    .bind(&data.method)
    .bind(&data.serialize_headers())
    .bind(&data.status_code)
    .bind(&data.content).execute(&mut *tx).await {
        tx.commit().await?;
        Ok(res.rows_affected() as i32)
    }else{
        tx.rollback().await?;
        Err(AdminOpError::InternalError("open transaction error".to_string()))
    }
}

pub async fn del_route(pool: Pool<MySql>, id: i32) -> Result<i32, AdminOpError> {
    let mut tx = pool.begin().await?;
    let res = sqlx::query("select * from mock_routers where id = ?")
        .bind(id)
        .fetch_all(&mut *tx)
        .await?;
    if res.len() == 0 {
        tx.rollback().await?;
        return Err(AdminOpError::RecordNotExist(format!("{}", id)));
    }

    if let Ok(res) = sqlx::query("delete from mock_routers where id = ?")
        .bind(&id)
        .execute(&mut *tx)
        .await
    {
        tx.commit().await?;
        Ok(res.rows_affected() as i32)
    } else {
        tx.rollback().await?;
        Err(AdminOpError::InternalError(
            "open transaction error".to_string(),
        ))
    }
}
